package org.crag.cafca.logic.time;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

import org.apache.log4j.Logger;
import org.crag.cafca.logic.Family;
import org.crag.cafca.logic.FamilyMember;
import org.crag.cafca.logic.time.SourceManagementEvent;
import org.crag.cafca.logic.time.Tag;
import org.joda.time.DateTime;


public class TimeLineManager 
{
	/**
	 * Logger log4j. See log4j doc for more information.
	 */
	private static Logger log = Logger.getLogger(new Throwable().getStackTrace()[0].getClassName());

	public Map<String,Source> getSources(DateTime dateTime,TimeLine timeLine)
	{
		Map<String,Source> result=new HashMap<String,Source>();
		
		for (Event event:timeLine.getEvents())
		{
			if (dateTime!=null&&event.getStartDateTime().isAfter(dateTime))
				break;
			
			if (event instanceof SourceManagementEvent)
			{
				SourceManagementEvent sourceManagementEvent=(SourceManagementEvent)event;
				String sourceId=sourceManagementEvent.getSourceId();
				
				if (sourceManagementEvent.containsTag(new Tag("NEW_SOURCE")))
				{
					Source newSource=new Source(sourceId);
					Set<Tag> sourceTags=new HashSet<Tag>();
					sourceTags.addAll(sourceManagementEvent.getTags());
					sourceTags.remove(new Tag("NEW_SOURCE"));
					newSource.addTags(sourceTags);
					result.put(sourceId, newSource);
				}
				
				if (sourceManagementEvent.containsTag(new Tag("DELETE_SOURCE")))
				{
					result.remove(sourceId);
				}
			}
		}
		
		return result;
	}
	
	public Family getFamily(DateTime dateTime, TimeLine timeLine)
	{
		Family result=new Family(timeLine.getFamilyName());
		
		for (Event event:timeLine.getEvents())
		{
			if (event.getStartDateTime().isAfter(dateTime))
				break;
			
			if (event instanceof FamilyEvent)
			{
				FamilyEvent familyEvent=(FamilyEvent)event;
				
				if (familyEvent.containsTag(new Tag("NEW_MEMBER")))
				{
					String familyMemberName=familyEvent.getValue();
					FamilyMember newMember=new FamilyMember(familyMemberName);
					result.addMember(newMember);
				}
				
				if (familyEvent.containsTag(new Tag("DELETE_MEMBER")))
				{
					String familyMemberName=familyEvent.getValue();
					result.removeMember(familyMemberName);
				}
			}
		}
		
		return result;
	}
	
	public SortedSet<SourceEvent> getSourceEvents(String sourceId,TimeLine timeLine)
	{
		TreeSet<SourceEvent> result=new TreeSet<SourceEvent>(Event.getEventsComparator());
		
		for (Event event:timeLine.getEvents())
		{
			if (event instanceof SourceEvent)
			{
				SourceEvent sourceEvent=(SourceEvent)event;
				if (sourceEvent.getSourceId().equals(sourceId))
				{
					result.add(sourceEvent);
				}
			}
		}
		
		return result;
	}
	
	public SortedSet<SourceEvent> getSourceEventsContainingTag(String sourceId,Tag tag,TimeLine timeLine)
	{
		TreeSet<SourceEvent> result=new TreeSet<SourceEvent>(Event.getEventsComparator());
		
		for (Event event:timeLine.getEvents())
		{
			if (!event.containsTag(tag))
				continue;
			
			if (event instanceof SourceEvent)
			{
				SourceEvent sourceEvent=(SourceEvent)event;
				if (sourceEvent.getSourceId().equals(sourceId))
				{
					result.add(sourceEvent);
				}
			}
		}
		return result;		
	}
	
	public SortedSet<SourceEvent> getSourceEventsContainingTag(Tag tag,TimeLine timeLine)
	{
		TreeSet<SourceEvent> result=new TreeSet<SourceEvent>(Event.getEventsComparator());
		
		for (Event event:timeLine.getEvents())
		{
			if (!event.containsTag(tag))
				continue;
			
			if (event instanceof SourceEvent)
			{
				SourceEvent sourceEvent=(SourceEvent)event;
				result.add(sourceEvent);
			}
		}
		return result;		
	}
	
	public boolean checkConsistency(TimeLine timeLine)
	{
		log.debug("Consistency check on the timeLine: "+timeLine.getFamilyName());
		Map<String,Source> sources=new HashMap<String,Source>();
		
		for (Event event:timeLine.getEvents())
		{
			if (event instanceof SourceManagementEvent)
			{
				SourceManagementEvent sourceManagementEvent=(SourceManagementEvent)event;
				String sourceId=sourceManagementEvent.getSourceId();
				
				if (sourceManagementEvent.containsTag(new Tag("NEW_SOURCE")))
				{
					Source newSource=new Source(sourceId);
					Set<Tag> sourceTags=new HashSet<Tag>();
					sourceTags.addAll(sourceManagementEvent.getTags());
					sourceTags.remove(new Tag("NEW_SOURCE"));
					newSource.addTags(sourceTags);
					sources.put(sourceId, newSource);
				}
				
				if (sourceManagementEvent.containsTag(new Tag("DELETE_SOURCE")))
				{
					sources.remove(sourceId);
				}
			}
			
			if (event instanceof SourceEvent)
			{
				SourceEvent sourceEvent=(SourceEvent)event;
				
				if (!sources.containsKey(sourceEvent.getSourceId()))
				{
					log.debug("Consistency check failed. The event: "+sourceEvent+" does not refer to an opened source.");
					return false;
				}
			}
		}
		log.debug("Consistency check passed");
		return true;
	}
}
